#include "StdAfx.h"
#include "Approach.h"
#include "EphemWrapper.h"

CApproach::CApproach(eBodies bodyNum, double dTime,const Body &body, double dXdiff[3], double dParam)
: m_bodyNum(bodyNum)
, m_dTime(dTime)
, m_body(body)
, m_dParam(dParam)
{
	memcpy(m_dXdiff, dXdiff, sizeof(m_dXdiff));
	printf("!!!Found at %s, dist %lf km\n", CEphemWrapper::StrDateFromJD(dTime).c_str(), CEphemWrapper::AUtoKM(Dist()));

	
	m_dDist = 0.0;
	for(int i = 0; i < 3; i++)
	{
		m_dDist += m_dXdiff[i] * m_dXdiff[i];
	}
	m_dDist = sqrt(m_dDist);
}

CApproach::~CApproach(void)
{
}

double CApproach::Dist() const
{
	return m_dDist;
}

bool CApproach::CompareApproachesByTime(Ptr a, Ptr b)
{
	return a->m_dTime < b->m_dTime;
}

bool CApproach::CompareApproachesByTimeParam(Ptr a, Ptr b)
{
	if( a->m_dTime == b->m_dTime)
		return a->m_dParam < b->m_dParam;
	else
		return a->m_dTime < b->m_dTime;
}

// ================================= //

COneBodyApproachSearcher::COneBodyApproachSearcher(ISimpleIntegratorInterface * pIntegrator, IGetCoord * pEphem, eBodies nBodyNum, double dParam)
: m_nBodyNum(nBodyNum)
, m_dDetectDist(0.2) // 0.2 au
, m_dSerchAccuracy(1.0e-6) // day,  ~ 0.1 sec
, m_pIntegrator(pIntegrator)
, m_pEphem(pEphem)
, m_dParam(dParam)
{
	m_dPrevDist = m_dDetectDist * 10.0;
	m_bWasApproach = false;
	m_dPrevTime = 0.0;

	if(nBodyNum >= BODY_COUNT)
		throw CCustomException(_T("body num too big, must be less then %d"), BODY_COUNT);
	if(m_pIntegrator == NULL)
		throw CCustomException(_T("empty rada pointer"));
	if(m_pEphem == NULL)
		throw CCustomException(_T("empty Eptem pointer"));
};

CApproach::Ptr COneBodyApproachSearcher::Check(double dTime, const Body & body)
{
	CApproach::Ptr app;
	if( m_dPrevTime == dTime)
	{
		return app;
	}

	double XDif[3] = {0};
	double curdist = GetDistance(dTime, body, XDif);

	if(curdist > m_dDetectDist)
	{
		m_bWasApproach = false;
		m_dPrevDist = m_dDetectDist * 10.0;
		return CApproach::Ptr();
	}

	//printf("\t\t\t\t %lf %lf %lf %d\n", dTime, curdist, m_dPrevDist- curdist, m_bWasApproach);
	
	if ( m_dPrevDist < curdist ) 
	{
		if ( !m_bWasApproach ) 
		{
			app = Search(dTime, body, dTime - m_dPrevTime);
			m_bWasApproach = true;
		}
	}
	else
	{
		m_bWasApproach = false;
	}
	m_dPrevDist = curdist;
	m_dPrevTime = dTime;
	return app;
}

CApproach::Ptr COneBodyApproachSearcher::Search(double dTime, Body body, double dStep)
{
	double XDif[3];
	double prevDist = GetDistance(dTime, body, XDif);
	while( fabs(dStep) > m_dSerchAccuracy)
	{
		dStep *= -0.1;
		double curDist = 0;
		while(1)
		{
			m_pIntegrator->SimpleMove(dTime, dTime+dStep, body);
			dTime += dStep;
			curDist = GetDistance(dTime, body, XDif);
			
			double dprev = prevDist;
			prevDist = curDist;
			if(dprev < curDist)
				break;
		};
	}

	return CApproach::Ptr(new CApproach(m_nBodyNum, dTime, body, XDif, m_dParam));
}

double COneBodyApproachSearcher::GetDistance(double dTime, const Body & body, double dXdiff[3])
{
	AllBodies bodies;
	m_pEphem->GetCoord(dTime, true, bodies, false);
	const Body &curBody = bodies.bodies[m_nBodyNum];
	double dDist = 0;
	for(int i = 0; i < 3; i++)
	{
		dXdiff[i] = body.x[i] - curBody.x[i];
		dDist += dXdiff[i] * dXdiff[i];
	}
	dDist = sqrt(dDist);
	return dDist;
}

// ================================= //

CApproachSearcher::CApproachSearcher(ISimpleIntegratorInterface * pIntegrator, IGetCoord * pEphem, const vector<eBodies> & vecBodysToSearch, double dParam)
{
	for(size_t i=0; i<vecBodysToSearch.size(); ++i)
	{
		m_listSearcher.push_back(COneBodyApproachSearcher(pIntegrator, pEphem, vecBodysToSearch[i], dParam));
	}
}

ListApproachPtr CApproachSearcher::Check(double dTime, const Body & body)
{
	ListApproachPtr listApp;
	ListApprchSch::iterator it = m_listSearcher.begin();
	ListApprchSch::iterator end = m_listSearcher.end();
	for(; it != end; ++it)
	{
		COneBodyApproachSearcher & searcher = *it;
		CApproach::Ptr pApp = searcher.Check(dTime, body);
		if( pApp )
		{
			listApp.push_back(pApp);
		}
	}
	listApp.sort(CApproach::CompareApproachesByTime);
	return listApp;
}